﻿using HtmlAgilityPack;
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;

namespace WhatCatch.Core
{
    public class ListPageHandler : IListPageHandler
    {
        CrawlerConfig _config;
        DataContainerBase _dataContainer;
        IHttpHandler _httpHandler;
        ICrawlerLog _crawlerLog;
        public ListPageHandler(CrawlerConfig config, DataContainerBase dataContainer, IHttpHandler httpHandler, ICrawlerLog crawlerLog)
        {
            _config = config;
            _dataContainer = dataContainer;
            _httpHandler = httpHandler;
            _crawlerLog = crawlerLog;
        }

        /// <summary>
        /// 处理一次列表页任务
        /// </summary>
        public void HandleTask(TaskContext task)
        {
            try
            {
                _crawlerLog.Info($"【线程{task.ThreadIndex}】处理页面 {task.Url}");

                //构造一个上下文
                RequestContext requestContext = new RequestContext();
                requestContext.Url = task.Url;

                //获取页面内容
                string html = "";
                try
                {
                    html = _httpHandler.GetHtml(requestContext.Url);
                }
                catch (Exception ex)
                {
                    _crawlerLog.Error($"【线程{task.ThreadIndex}】异常：" + ex.ToString());
                }
                if (string.IsNullOrEmpty(html))
                {
                    _crawlerLog.Info($"【线程{task.ThreadIndex}】url:{requestContext.Url}获取html为空");
                    Thread.Sleep(1000);
                    return;
                }
                requestContext.Html = html;

                //解析页面
                HtmlDocument htmlDoc = new HtmlDocument();
                htmlDoc.OptionAutoCloseOnEnd = true;
                htmlDoc.LoadHtml(requestContext.Html);
                requestContext.HtmlDocument = htmlDoc;

                //解析出明细列表
                var listItemUrl = AnalysisItemUrlList(requestContext);
                if (listItemUrl == null || listItemUrl.Count == 0)
                    return;
                requestContext.ItemUrls = listItemUrl;
                //解析出分页列表
                var listPageUrl = AnalysisPageUrlList(requestContext);
                if (listPageUrl == null || listPageUrl.Count == 0)
                    return;
                requestContext.PageUrls = listPageUrl;

                //结果存储
                foreach (var li in requestContext.ItemUrls)
                {
                    //添加明细任务（会判重）
                    _dataContainer.AddDetailUrl(li);
                }
                foreach (var li in requestContext.PageUrls)
                {
                    //任务入队（会判重）
                    _dataContainer.AddListUrl(li);
                }
            }
            catch (Exception ex)
            {
                _crawlerLog.Error($"【线程{task.ThreadIndex}】处理一次列表页任务异常：" + ex.ToString());
            }
        }

        /// <summary>
        /// 解析列表项url列表
        /// </summary>
        /// <param name="requestContext">请求上下文</param>
        /// <returns></returns>
        private List<string> AnalysisItemUrlList(RequestContext requestContext)
        {
            Console.WriteLine("解析列表项url列表");

            //列表区域
            HtmlNode contentNode = null;
            if (!string.IsNullOrEmpty(_config.ListXPath))
            {
                contentNode = requestContext.HtmlDocument.DocumentNode.SelectSingleNode(_config.ListXPath);
            }
            else
            {
                contentNode = requestContext.HtmlDocument.DocumentNode;
            }
            requestContext.ListHtml = contentNode.OuterHtml;

            //列表项
            HtmlNodeCollection listNode = null;
            if (string.IsNullOrEmpty(_config.ListItemXPath))//未指定xpath
            {
                listNode = contentNode.SelectNodes("descendant::div|descendant::li|descendant::tr");

                var listItemUrl = new List<string>();

                return listItemUrl;
            }
            else//指定xpath
            {
                listNode = contentNode.SelectNodes(_config.ListItemXPath);
                if (listNode == null)
                {
                    throw new Exception("找不到项");
                }

                var listItemUrl = new List<string>();
                foreach (var item in listNode)
                {
                    var url = GetUrl(item.OuterHtml, requestContext.Url);
                    listItemUrl.Add(url);
                }
                //requestContext.ItemUrls = listItemUrl;
                return listItemUrl;
            }
        }

        private string GetUrl(string txt,string baseUrl)
        {

            var mcs = new Regex(@"<a[^>]*?>(?<title>[\s\S]*?)</a>", RegexOptions.IgnoreCase).Matches(txt);
            string title = "";
            foreach (Match mc in mcs)
            {
                title = mc.Groups["title"].Value;
                if (title == "" || title.Length <= 4 || title.Contains("<div") || title.Contains("<img"))
                {
                    continue;
                }
                else
                {
                    var mc2 = new Regex(@"<a[^>]+href=\s*(?:'(?<href>[^']+)'|""(?<href>[^""]+)""|(?<href>[^>\s]+))\s*[^>]*>(?<text>.*?)</a>", RegexOptions.IgnoreCase | RegexOptions.Singleline).Match(mc.Value);
                    string url = "";
                    if (mc2.Success)
                    {
                        url = mc2.Groups["href"].Value;
                        url = HandleUrl(baseUrl, url);
                    }
                    return url;
                }
            }
            return "-1";
        }

        private string HandleUrl(string baseUrl, string url)
        {
            if (!string.IsNullOrEmpty(baseUrl))
            {
                Uri baseUri = new Uri(baseUrl);
                Uri absoluteUri = new Uri(baseUri, url);//相对绝对路径都在这里转 这里的urlx ="../test.html"
                return absoluteUri.ToString();
            }
            else
            {
                return url;
            }
        }

        /// <summary>
        /// 解析分页url列表
        /// </summary>
        /// <param name="requestContext">请求上下文</param>
        /// <returns></returns>
        private List<string> AnalysisPageUrlList(RequestContext requestContext)
        {
            Console.WriteLine("解析分页url列表");

            //分页区域
            HtmlNode contentNode = null;
            if (!string.IsNullOrEmpty(_config.PageXPath))
            {
                contentNode = requestContext.HtmlDocument.DocumentNode.SelectSingleNode(_config.PageXPath);
            }
            else
            {
                contentNode = requestContext.HtmlDocument.DocumentNode;
            }
            requestContext.PageHtml = contentNode.OuterHtml;

            //列表项
            HtmlNodeCollection listPageNode = null;
            if (string.IsNullOrEmpty(_config.PageItemXPath))//未指定xpath
            {
                listPageNode = contentNode.SelectNodes("descendant::div|descendant::li|descendant::tr|descendant::a");

                var listPageItemUrl = new List<string>();

                return listPageItemUrl;
            }
            else//指定xpath
            {
                listPageNode = contentNode.SelectNodes(_config.PageItemXPath);
                if (listPageNode == null)
                {
                    throw new Exception("找不到分页项");
                }

                var listPageItemUrl = new List<string>();
                foreach (var item in listPageNode)
                {
                    var url = GetPageUrl(item.OuterHtml, requestContext.Url);
                    listPageItemUrl.Add(url);
                }
                //requestContext.PageUrls = listPageItemUrl;
                return listPageItemUrl;
            }

           
        }

        private string GetPageUrl(string txt, string baseUrl)
        {
            var matches = new Regex(@"<a[^>]+href=\s*(?:'(?<href>[^']+)'|""(?<href>[^""]+)""|(?<href>[^>\s]+))\s*[^>]*>(?<text>.*?)</a>", RegexOptions.IgnoreCase | RegexOptions.Singleline).Matches(txt);
            foreach (Match m in matches)
            {
                string url = m.Groups["href"].Value;
                url = HandleUrl(baseUrl, url);

                url = url.Replace("&amp;", "&");

                if (url.IndexOf("script") >= 0)
                {
                    continue;
                }

                return url;
            }
            return "";
        }
    }
}
